package com.ld.shieldsb.sensitiveword.service.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import com.ld.shieldsb.common.core.collections.ListUtils;
import com.ld.shieldsb.common.core.io.FileUtils;
import com.ld.shieldsb.common.core.io.IOUtils;
import com.ld.shieldsb.common.core.model.PageBean;
import com.ld.shieldsb.common.core.model.Result;
import com.ld.shieldsb.common.core.reflect.ClassUtil;
import com.ld.shieldsb.common.core.util.PathUtil;
import com.ld.shieldsb.common.core.util.StringUtils;
import com.ld.shieldsb.sensitiveword.Dictionary;
import com.ld.shieldsb.sensitiveword.DictionaryItem;
import com.ld.shieldsb.sensitiveword.SensitiveWordType;
import com.ld.shieldsb.sensitiveword.service.SensitiveWordService;

import lombok.extern.slf4j.Slf4j;

/**
 * 敏感词处理器工具（文件服务）
 * 
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2019年10月28日 上午8:41:08
 *
 */
@Slf4j
public class SensitiveWordFileService extends SensitiveWordService {
    public static final String EXT_DEF_FILE_NAME = "ext-def.dic"; // 扩展库文件名
    public static final String EXT_DEMO_FILE_NAME = "ext-demo.dic"; // 示例扩展库文件名

    static {
        // 创建一个扩展库，词库最后加载，用于不解压默认配置时覆盖默认配置
        File configFile = getExtDefaultFile(); // 判断文件是否存在
        if (configFile != null && !configFile.exists()) {
            try {
                URL resourceURL = SensitiveWordFileService.class.getClassLoader().getResource("/" + EXT_DEMO_FILE_NAME);
                if (resourceURL != null) {
                    List<String> lines = IOUtils.readLines(resourceURL.openStream(), "UTF-8"); // 去除缓存的写法
                    List<String> linesWrite = new ArrayList<>(); // 去除缓存的写法
                    lines.stream().forEach(line -> {
                        String str = line;
                        if (line.contains("用户扩展字典库示例")) {
                            str = line.replace("用户扩展字典库示例", "默认扩展字典库");
                        }
                        linesWrite.add(str);
                    });
                    try (FileOutputStream outStream = new FileOutputStream(configFile)) {
                        IOUtils.writeLines(linesWrite, IOUtils.LINE_SEPARATOR, outStream, "UTF-8");
                    }
                }
            } catch (IOException e) {
                log.error("", e);
            }
        }
    }

    public static File getExtDefaultFile() {
        String classRootPath = PathUtil.getClassRootPath(true);
        if (!StringUtils.isEmpty(classRootPath)) {
            File configFile = new File(classRootPath, EXT_DEF_FILE_NAME); // 判断文件是否存在
            return configFile;
        }
        return null;
    }

    /**
     * 从配置文件中加载字典
     * 
     * @Title loadDict
     * @author 吕凯
     * @date 2019年10月28日 上午9:16:25 void
     */
    @Override
    public void loadDict() {
        List<String> dicts = new ArrayList<>();
        String dictStr = getString(CONFIG_SERVICE_DICT_EMBED, null); // 内置字典，取配置文件中的信息
        if (StringUtils.isNotEmpty(dictStr)) {
            String[] dictArrs = dictStr.split(",");
            for (int i = 0; i < dictArrs.length; i++) {
                String dictName = dictArrs[i];
                dicts.add(dictName);
//                Dictionary dict = getDictionary(dictName); // 获取字典对象
//                loadDictionary(dict);
            }

        }
        String extDictStr = getString(CONFIG_SERVICE_DICT_EXT, null); // 扩展字典，文件路径或url
        if (StringUtils.isNotEmpty(extDictStr)) {
            String[] dictArrs = extDictStr.split(",");
            for (int i = 0; i < dictArrs.length; i++) {
                String dictName = dictArrs[i];
                dicts.add(dictName);
            }

        }
        loadDict(dicts);
    }

    @Override
    public void loadDict(List<String> dicts) {

        dictionarys.clear(); // 清空已经加载的字典库
        clearDataCollection(); // 清空集合中的数据防止干扰

        if (StringUtils.isNotEmpty(dicts)) {
            for (String dictName : dicts) {
                File file = new File(dictName);
                Dictionary dict = null;
                if (file.exists()) { // 查看是否是外置文件，如果不是则找内置库
                    dict = getDictionary(file); // 获取字典对象
                } else {
                    dict = getDictionary(dictName); // 获取字典对象
                }
                if (dict == null) {
                    log.error("字典库文件不存在：" + dictName);
                } else {
                    loadDictionary(dict);
                }

            }
        }

        // 默认扩展词库
        File file = getExtDefaultFile();
        if (file != null && file.exists()) { // 存在才加载，不存在则不处理
            Dictionary dict = getDictionary(file); // 获取字典对象
            loadDictionary(dict);
        } else {
            log.error("默认扩展字典库文件不存在：" + (file != null ? file.getAbsolutePath() : EXT_DEF_FILE_NAME));
        }

        handler.init(sensitiveWordSet, replaceWordMap);
    }

    @Override
    public String getDictName(String dictName) {
        if (!dictName.endsWith(".dic")) { // 内置字典，可以不以.dic结尾，自动补全
            dictName += ".dic";
        }
        dictName = StringUtils.replace(dictName, "\\", "/"); // 路径统一转换为/
        return dictName;
    }

    // 清空数据集合
    private void clearDataCollection() {
        sensitiveWordSet = new LinkedHashSet<>();
        replaceWordMap = new LinkedHashMap<>();
        wordDictionaryMap = new LinkedHashMap<>();
    }

    /**
     * 更新字典库
     * 
     * @Title updateDict
     * @author 吕凯
     * @date 2019年10月28日 下午5:10:01
     * @param file
     *            void
     */
    @Override
    public void updateDict(File file) {
        if (file.exists()) {
            Dictionary dict = getDictionary(file); // 获取字典对象
            dictionarys.put(dict.getFileName(), dict); // 放置到字典缓存中。
            clearDataCollection();
            dictionarys.entrySet().stream().forEach(r -> loadDictionary(r.getValue())); // 重新加载
            handler.init(sensitiveWordSet, replaceWordMap);
        }

    }

    /**
     * 将词典加载进处理器中
     * 
     * @Title loadDictionary
     * @author 吕凯
     * @date 2019年10月28日 上午11:24:04
     * @param dict
     *            void
     */
    @Override
    public void loadDictionary(Dictionary dict) {
        if (dict != null) {
            String dictFileName = dict.getFileName();
            String dictName = dictFileName;
//            String dictName = StringUtils.replace(dictFileName, "\\", "/"); // 路径统一转换为/
            List<DictionaryItem> items = dict.getItems();
            if (ListUtils.isNotEmpty(items)) {
                items.stream().forEach(item -> {
                    boolean isRemove = item.isRemove();
                    String word = item.getWord();
                    wordDictionaryMap.put(word, dictName); // 保存关键词和字典的对应关系
                    if (isRemove) { // 移除
                        sensitiveWordSet.remove(word);
                        replaceWordMap.remove(word);
                    } else {
                        sensitiveWordSet.add(word);
                        replaceWordMap.remove(word, item.getReplaceWord()); // 先移除（防止存在覆盖类型变化的情况）
                        if (item.getType() == SensitiveWordType.REPLACE.getValue()) {
                            replaceWordMap.put(word, item.getReplaceWord());
                        }
                    }
                });
            }
            log.warn("加载字典" + dictName);
            dictionarys.put(dictName, dict); // 放置到字典缓存中
        }
    }

    // 卸载，因为存在多个字典库覆盖的问题，这种卸载可能有问题不如直接重写加载所有字典库，比如在词库中覆盖了某个词语，此方法移除后该词语就不存在了，这是有问题的
    @Override
    public void removeDictionary(Dictionary dict) {
        if (dict != null) {
            String dictName = dict.getFileName();
            dictionarys.remove(dictName);
            boolean containsDic = wordDictionaryMap.containsValue(dictName);
            if (containsDic) {
                List<String> keys = new ArrayList<>();
                wordDictionaryMap.entrySet().stream().filter(r -> r.getValue().equalsIgnoreCase(dictName)).forEach(entry -> {
                    keys.add(entry.getKey());
                });
                keys.stream().forEach(word -> {
                    // 获取字典对象中该词条对象
                    DictionaryItem item = dict.getItem(word);
                    if (item.getType() == SensitiveWordType.FORBID.getValue()) {
                        // 如果为删除
                        sensitiveWordSet.remove(word);
                        replaceWordMap.remove(word);
                        wordDictionaryMap.remove(word);
                    }
                });
            }
        }
    }

    /**
     * 
     * 根据文件名称获取字典库对象
     * 
     * @Title getDictionary
     * @author 吕凯
     * @date 2019年10月28日 上午10:42:08
     * @param dictName
     *            相对class的路径
     * @return Dictionary
     */
    @Override
    public Dictionary getDictionary(String dictName) {
        Dictionary dict = new Dictionary();
        dictName = getDictName(dictName);
        dict.setFileName(dictName);
        dict.setFilePath(dictName);
        List<String> lines = getLines(dictName);
        fillDict(dict, lines);
        return dict;
    }

    private void fillDict(Dictionary dict, List<String> lines) {
        if (ListUtils.isNotEmpty(lines)) {
            lines = lines.stream().filter(lineStr -> StringUtils.isNotBlank(lineStr)).collect(Collectors.toList());
//              List<String> dictItems = lines.stream().filter(lineStr -> StringUtils.isNotBlank(lineStr) && !lineStr.startsWith("###")
//                      && !lineStr.startsWith("name=") && !lineStr.startsWith("version=")).collect(Collectors.toList());
            List<DictionaryItem> items = dict.getOrCreate();
            items.clear();
            // 进一步解析
            lines.stream().forEach(lineStr -> {
                lineStr = lineStr.trim();
                if (StringUtils.isBlank(lineStr)) { // 非空
                    return;// 起到的作用和continue是相同的
                }
                if (lineStr.startsWith("###")) { // 描述
                    dict.setDescription(StringUtils.substringAfter(lineStr, "###"));
                } else if (lineStr.startsWith("name=")) {
                    dict.setName(StringUtils.substringAfter(lineStr, "name=")); // 名称
                } else if (lineStr.startsWith("version=")) {
                    dict.setVersion(StringUtils.substringAfter(lineStr, "version=")); // 版本
                } else {
                    String lineKey = lineStr;
                    String lineReplace = null;
                    boolean lineRemove = false;
                    if (lineStr.endsWith(SensitiveWordType.REMOVE.getValue() + "")) { // -1表示移除
                        lineRemove = true;
                    }
                    if (lineStr.contains("=") && !lineStr.contains("\\=")) { // 用\来转义等号
                        lineKey = StringUtils.substringBefore(lineStr, "=");
                        lineReplace = StringUtils.substringAfter(lineStr, "=");
                        if (lineRemove) {
                            lineReplace = StringUtils.substringBeforeLast(lineReplace, SensitiveWordType.REMOVE.getValue() + ""); // 移除-1
                        }
                    }
                    if (lineRemove && lineKey.contains(SensitiveWordType.REMOVE.getValue() + "")) {
                        lineKey = StringUtils.substringBeforeLast(lineKey, SensitiveWordType.REMOVE.getValue() + "");
                    }
                    DictionaryItem item = new DictionaryItem();
                    item.setWord(lineKey);
                    item.setReplaceWord(lineReplace);
                    item.setRemove(lineRemove);
                    item.setType(lineReplace == null ? SensitiveWordType.FORBID.getValue() : SensitiveWordType.REPLACE.getValue()); // 类型0禁止1替换
                    items.add(item);

                }
            });
        }
    }

    // 获取classes路径下文件的所有行内容，dictName为文件相对classes的相对路径名称
    private List<String> getLines(String dictName) {
        List<String> lines = null;
        try (InputStream dictIn = SensitiveWordFileService.class.getClassLoader().getResourceAsStream(dictName)) {
            lines = IOUtils.readLines(dictIn, "UTF-8");
        } catch (IOException e) {
            log.error("", e);
        }
        return lines;
    }

    // 获取文件的所有行内容
    private List<String> getLines(File file) {
        List<String> lines = null;
        try {
            lines = FileUtils.readLines(file, "UTF-8");
        } catch (IOException e) {
            log.error("", e);
        }
        return lines;
    }

    /**
     * 根据file对象获取字典库
     * 
     * @Title getDictionary
     * @author 吕凯
     * @date 2019年10月28日 下午4:45:17
     * @param dictFile
     * @return Dictionary
     */
    @Override
    public Dictionary getDictionary(File dictFile) {
        Dictionary dict = new Dictionary();
        dict.setFileName(dictFile.getName());
        String filePath = getDictName(dictFile.getAbsolutePath());
        dict.setFilePath(filePath);
        List<String> lines = getLines(dictFile);
        fillDict(dict, lines);
        return dict;
    }

    @Override
    public List<Dictionary> getDictsEmbedAll() { // 所有的内置库
        Set<String> dictionarysAllEmbed = getDictionarysAllEmbed(); // 所有内置库
        List<Dictionary> dictionarysEmbedShow = new ArrayList<>(); // 所有内置库
        dictionarysAllEmbed.forEach(dictName -> {
            String dictNameShow = dictName;
            // 内置字典，可以不以.dic结尾，自动补全
            dictName = getDictName(dictName);
            Dictionary dict = getDictionarys().get(dictName);
            if (dict == null) {
                dict = new Dictionary();
                dict.setFileName(dictNameShow);
            }
            dictionarysEmbedShow.add(dict);
        });
        return dictionarysEmbedShow;
    }

    @Override
    public List<Dictionary> getDictsEmbedLoad() { // 加载的内置库
        Set<String> dictionarysAllEmbed = getDictionarysAllEmbed(); // 所有内置库
        List<Dictionary> dictionarysEmbedShow = new ArrayList<>(); // 所有内置库
        dictionarysAllEmbed.forEach(dictName -> {
            // 内置字典，可以不以.dic结尾，自动补全
            dictName = getDictName(dictName);
            Dictionary dict = getDictionarys().get(dictName);
            if (dict != null) { // 对象存在说明加载了
                dictionarysEmbedShow.add(dict);
            }
        });
        return dictionarysEmbedShow;
    }

    @Override
    public List<Dictionary> getDictsExtLoad() {
        Set<String> dictionarysAllEmbed = getDictionarysAllEmbed(); // 所有内置库
        Set<String> dictionarysAllEmbedSet = new LinkedHashSet<>();
        dictionarysAllEmbed.stream().forEach(key -> {
            dictionarysAllEmbedSet.add(getDictName(key));
        });
        Set<String> dictionarysExtSet = new LinkedHashSet<>(); // 扩展库
        List<Dictionary> dictionarysExtShow = new ArrayList<>(); // 所有内置库
        if (ListUtils.isNotEmpty(dictionarys.keySet())) {
            Set<String> dictionarysKey = dictionarys.keySet();
            dictionarysExtSet.addAll(dictionarysKey);
            dictionarysExtSet.removeAll(dictionarysAllEmbedSet);
        }

        dictionarysExtSet.forEach(dictName -> {
            String dictNameShow = dictName;
            // 内置字典，可以不以.dic结尾，自动补全
            dictName = getDictName(dictName);
            Dictionary dict = getDictionarys().get(dictName);
            if (dict == null) {
                dict = new Dictionary();
                dict.setFileName(dictNameShow);
            }
            dictionarysExtShow.add(dict);
        });
        return dictionarysExtShow;
    }

    @Override
    public List<Dictionary> getDictsExtAll() {
        return getDictsExtLoad(); // 扩展库加载的即是全部
    }

    @Override
    public Result delDictItem(String dictFileName, String word, Map<String, Object> extendQueryModel) {
        Result result = new Result();
        Dictionary dictionary = dictionarys.get(dictFileName);
        if (dictionary == null) {
            result.setMessage("未加载的词库不能修改！");
            return result;
        }
        File file = new File(dictionary.getFilePath());
        if (!file.exists()) {
            result.setMessage("内置词库或文件不存在，不可删除，删除内置词库的词条可以在扩展库中增加类型为“移除”的词条！");
            return result;
        }
        try {
            List<String> lines = FileUtils.readLines(file, "utf-8");
            boolean hasWord = lines.stream().anyMatch(lineStr -> matchKeyWord(lineStr, word));
            if (!hasWord) {
                result.setMessage("当前词库不存在“" + word + "”关键词");
                return result;
            }
            lines = lines.stream().filter(lineStr -> !matchKeyWord(lineStr, word)).collect(Collectors.toList());
            FileUtils.writeLines(file, lines);
        } catch (IOException e) {
            log.error("", e);
        }
        updateDict(file); // 更新字典库
        result.setSuccess(true);
        result.setMessage("删除敏感词成功！");
        return result;
    }

    @Override
    public Result addDictItem(String dictFileName, DictionaryItem item, Map<String, Object> extendQueryModel) {
        Result result = new Result();
        File file = new File(dictFileName);
        if (!file.exists()) {
            result.setMessage("文件不存在！");
            return result;
        }
        String line = item.getWord();
        int type = ClassUtil.obj2int(item.getType());
        if (type == SensitiveWordType.REPLACE.getValue()) { // 替换
            line += "=" + item.getReplaceWord();
        } else if (type == SensitiveWordType.REMOVE.getValue()) { // 移除
            line += "-1";
        }
        try {
            List<String> lines = FileUtils.readLines(file, "utf-8");
            boolean hasWord = lines.stream().anyMatch(lineStr -> matchKeyWord(lineStr, item.getWord()));
            if (hasWord) { //
                result.setMessage("当前词库已经存在“" + item.getWord() + "”关键词");
                return result;
            }
            lines.add(line);
            FileUtils.writeLines(file, lines);
        } catch (IOException e) {
            log.error("", e);
        }
        updateDict(file);
        result.setSuccess(true);
        result.setMessage("添加敏感词成功！");
        return result;
    }

    /**
     * 获取行内容是否跟关键词匹配
     * 
     * @Title matchKeyWord
     * @author 吕凯
     * @date 2019年10月30日 上午8:53:55
     * @param line
     * @param word
     * @return boolean
     */
    private boolean matchKeyWord(String line, String word) {
        return line.equals(word) || line.startsWith(word + "=") || line.startsWith(word + "-1");
    }

    @Override
    public PageBean<DictionaryItem> getDictionaryItemPageBean(DictionaryItem queryModel, Map<String, Object> extendQueryModel, int pageNum,
            int pageSize) {
        PageBean<DictionaryItem> pageBean = new PageBean<DictionaryItem>();
        pageBean.setCurrentPage(pageNum);
        pageBean.setPageSize(pageSize);

        String dictName = queryModel.getDictionaryName();
        String wordSearch = queryModel.getWord();
        int typeSearch = queryModel.getType();
        if (StringUtils.isNotEmpty(dictName)) {
            // 内置字典，可以不以.dic结尾，自动补全
            String dictFileName = getDictName(dictName);
            Dictionary dict = getDictionarys().get(dictFileName);
            if (dict != null) {
                List<DictionaryItem> items = dict.getItems();
                // 根据条件进行过滤
                if (StringUtils.isNotEmpty(wordSearch) || typeSearch != INT_NULL) {
                    items = items.stream().filter(model -> {
                        boolean wordMath = true;
                        boolean typeMatch = true;
                        if (StringUtils.isNotEmpty(wordSearch)) {
                            wordMath = model.getWord().contains(wordSearch);
                        }
                        if (typeSearch != INT_NULL) {
                            typeMatch = model.getType() == typeSearch;
                        }
                        return wordMath && typeMatch;
                    }).collect(Collectors.toList());
                }
                int count = 0;
                List<DictionaryItem> list = null;
                if (ListUtils.isNotEmpty(items)) {
                    count = items.size();

                    int startNum = pageBean.getCurrentPoint() - 1;
                    int endNum = startNum + pageSize;
                    if (endNum > count) {
                        endNum = count;
                    }

                    list = items.subList(startNum, endNum);
                    list.stream().forEach(dictItem -> dictItem.setDictionaryName(dictFileName));
                }
                pageBean.setTotalCount(count);
                pageBean.setResultList(list);
            }
        } else { // 全部
            Set<String> sensitiveWordSet = getSensitiveWordSet();
            // 根据条件进行过滤
            if (StringUtils.isNotEmpty(wordSearch) || typeSearch != INT_NULL) {
                sensitiveWordSet = getSensitiveWordSet().stream().filter(word -> {
                    boolean wordMath = true;
                    boolean typeMatch = true;
                    if (StringUtils.isNotEmpty(wordSearch)) {
                        wordMath = word.contains(wordSearch);
                    }
                    if (typeSearch != INT_NULL) {
                        typeMatch = false;
                        if (typeSearch == SensitiveWordType.REPLACE.getValue()) { // 替换
                            typeMatch = getReplaceWordMap().get(word) != null;
                        } else if (typeSearch == SensitiveWordType.FORBID.getValue()) {
                            typeMatch = getReplaceWordMap().get(word) == null;
                        }

                    }
                    return wordMath && typeMatch;
                }).collect(Collectors.toSet());
            }
            int count = 0;
            List<DictionaryItem> list = new ArrayList<>();
            if (ListUtils.isNotEmpty(sensitiveWordSet)) {
                count = sensitiveWordSet.size();
                List<String> items = new ArrayList<>();
                items.addAll(sensitiveWordSet);
                List<String> resultWordList = new ArrayList<>();

                int startNum = pageBean.getCurrentPoint() - 1;
                int endNum = startNum + pageSize;
                if (endNum > count) {
                    endNum = count;
                }
                resultWordList = items.subList(startNum, endNum); // 获取敏感词
                if (ListUtils.isNotEmpty(resultWordList)) {
                    resultWordList.forEach(word -> {
                        // 组装为对象
                        DictionaryItem item = new DictionaryItem();
                        item.setWord(word);
                        item.setReplaceWord(getReplaceWordMap().get(word));
                        if (item.getReplaceWord() != null) {
                            item.setType(SensitiveWordType.REPLACE.getValue());
                        } else {
                            item.setType(SensitiveWordType.FORBID.getValue());
                        }
                        item.setDictionaryName(getWordDictionaryMap().get(word));
                        list.add(item);
                    });
                }
            }
            pageBean.setTotalCount(count);
            pageBean.setResultList(list);
        }

        return pageBean;
    }

}
